From: Stefano Stabellini Date: Tue, 10 Jun 2014 14:07:20 +0000 (+0100) Subject: xen/arm: gic_events_need_delivery and irq priorities X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~4841 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=72eaf29e8d70784aaf066ead79df1295a25ecfd0;p=xen.git xen/arm: gic_events_need_delivery and irq priorities Introduce GIC_IRQ_GUEST_ACTIVE to track which irqs are currently active in the guest. gic_events_need_delivery should only return positive if an outstanding pending irq has an higher group priority than the currently active group priotity and the priority mask. Read GICH_APR to find the active group priority. Read GICH_VMCR to find the priority mask. Find the highest priority non-active enabled irq by going through the inflight list. In gic_restore_pending_irqs replace lower priority pending (and not active) irqs in GICH_LRs with higher priority irqs if no more GICH_LRs are available. Signed-off-by: Stefano Stabellini Acked-by: Ian Campbell --- diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c index 4d044cf0b7..4d2a92d646 100644 --- a/xen/arch/arm/gic.c +++ b/xen/arch/arm/gic.c @@ -656,6 +656,7 @@ static void gic_update_one_lr(struct vcpu *v, int i) p = irq_to_pending(v, irq); if ( lr & GICH_LR_ACTIVE ) { + set_bit(GIC_IRQ_GUEST_ACTIVE, &p->status); if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) && test_and_clear_bit(GIC_IRQ_GUEST_QUEUED, &p->status) ) { @@ -679,6 +680,7 @@ static void gic_update_one_lr(struct vcpu *v, int i) if ( p->desc != NULL ) p->desc->status &= ~IRQ_INPROGRESS; clear_bit(GIC_IRQ_GUEST_VISIBLE, &p->status); + clear_bit(GIC_IRQ_GUEST_ACTIVE, &p->status); p->lr = GIC_INVALID_LR; if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) && test_bit(GIC_IRQ_GUEST_QUEUED, &p->status) ) @@ -712,20 +714,55 @@ void gic_clear_lrs(struct vcpu *v) static void gic_restore_pending_irqs(struct vcpu *v) { - int i; - struct pending_irq *p, *t; + int lr = 0, lrs = nr_lrs; + struct pending_irq *p, *t, *p_r; + struct list_head *inflight_r; unsigned long flags; spin_lock_irqsave(&v->arch.vgic.lock, flags); + + if ( list_empty(&v->arch.vgic.lr_pending) ) + goto out; + + inflight_r = &v->arch.vgic.inflight_irqs; list_for_each_entry_safe ( p, t, &v->arch.vgic.lr_pending, lr_queue ) { - i = find_first_zero_bit(&this_cpu(lr_mask), nr_lrs); - if ( i >= nr_lrs ) return; + lr = find_next_zero_bit(&this_cpu(lr_mask), nr_lrs, lr); + if ( lr >= nr_lrs ) + { + /* No more free LRs: find a lower priority irq to evict */ + list_for_each_entry_reverse( p_r, inflight_r, inflight ) + { + if ( p_r->priority == p->priority ) + goto out; + if ( test_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status) && + !test_bit(GIC_IRQ_GUEST_ACTIVE, &p_r->status) ) + goto found; + } + /* We didn't find a victim this time, and we won't next + * time, so quit */ + goto out; + +found: + lr = p_r->lr; + p_r->lr = GIC_INVALID_LR; + set_bit(GIC_IRQ_GUEST_QUEUED, &p_r->status); + clear_bit(GIC_IRQ_GUEST_VISIBLE, &p_r->status); + gic_add_to_lr_pending(v, p_r); + inflight_r = &p_r->inflight; + } - gic_set_lr(i, p, GICH_LR_PENDING); + gic_set_lr(lr, p, GICH_LR_PENDING); list_del_init(&p->lr_queue); - set_bit(i, &this_cpu(lr_mask)); + set_bit(lr, &this_cpu(lr_mask)); + + /* We can only evict nr_lrs entries */ + lrs--; + if ( lrs == 0 ) + break; } + +out: spin_unlock_irqrestore(&v->arch.vgic.lock, flags); } @@ -742,8 +779,40 @@ void gic_clear_pending_irqs(struct vcpu *v) int gic_events_need_delivery(void) { - return (!list_empty(¤t->arch.vgic.lr_pending) || - this_cpu(lr_mask)); + struct vcpu *v = current; + struct pending_irq *p; + unsigned long flags; + const unsigned long apr = GICH[GICH_APR]; + int mask_priority; + int active_priority; + int rc = 0; + + mask_priority = (GICH[GICH_VMCR] >> GICH_VMCR_PRIORITY_SHIFT) & GICH_VMCR_PRIORITY_MASK; + active_priority = find_next_bit(&apr, 32, 0); + + spin_lock_irqsave(&v->arch.vgic.lock, flags); + + /* TODO: We order the guest irqs by priority, but we don't change + * the priority of host irqs. */ + + /* find the first enabled non-active irq, the queue is already + * ordered by priority */ + list_for_each_entry( p, &v->arch.vgic.inflight_irqs, inflight ) + { + if ( GIC_PRI_TO_GUEST(p->priority) >= mask_priority ) + goto out; + if ( GIC_PRI_TO_GUEST(p->priority) >= active_priority ) + goto out; + if ( test_bit(GIC_IRQ_GUEST_ENABLED, &p->status) ) + { + rc = 1; + goto out; + } + } + +out: + spin_unlock_irqrestore(&v->arch.vgic.lock, flags); + return rc; } void gic_inject(void) diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h index 4900ef3882..2cb68374f4 100644 --- a/xen/include/asm-arm/domain.h +++ b/xen/include/asm-arm/domain.h @@ -57,8 +57,9 @@ struct pending_irq * */ #define GIC_IRQ_GUEST_QUEUED 0 -#define GIC_IRQ_GUEST_VISIBLE 1 -#define GIC_IRQ_GUEST_ENABLED 2 +#define GIC_IRQ_GUEST_ACTIVE 1 +#define GIC_IRQ_GUEST_VISIBLE 2 +#define GIC_IRQ_GUEST_ENABLED 3 unsigned long status; struct irq_desc *desc; /* only set it the irq corresponds to a physical irq */ int irq; diff --git a/xen/include/asm-arm/gic.h b/xen/include/asm-arm/gic.h index a14cd5e257..8e37ccf8da 100644 --- a/xen/include/asm-arm/gic.h +++ b/xen/include/asm-arm/gic.h @@ -129,6 +129,9 @@ #define GICH_LR_CPUID_SHIFT 9 #define GICH_VTR_NRLRGS 0x3f +#define GICH_VMCR_PRIORITY_MASK 0x1f +#define GICH_VMCR_PRIORITY_SHIFT 27 + /* * The minimum GICC_BPR is required to be in the range 0-3. We set * GICC_BPR to 0 but we must expect that it might be 3. This means we